package com.jeesite.modules.config;

import com.alibaba.fastjson.JSONObject;
import com.jeesite.common.config.Global;
import com.jeesite.common.lang.StringUtils;
import com.jeesite.modules.stock.service.RequestLimitCache;
import com.jeesite.modules.sys.utils.UserUtils;
import com.jeesite.modules.utils.NVL;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.naming.AuthenticationException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

@Component
@Slf4j
public class RequestInteceptor implements HandlerInterceptor {

    public static String args = "";

    @Autowired
    private RedisTemplate<Object, Object> redisTemplate;

    private void shutDown() {
        System.exit(-1);
    }

    /**
     * 将IP加入黑名单
     *
     * @param ip IP地址
     */
    private void addBlackList(String ip) {
        redisTemplate.opsForValue().set(ip, 1, 1, TimeUnit.HOURS);
    }

    /**
     * 是否是黑名单IP
     *
     * @param ip IP地址
     * @return true or false
     */
    private boolean isBlackIp(String ip) {
        Object o = redisTemplate.opsForValue().get(ip);
        return o != null;
    }

    private synchronized void ipLimit(HttpServletRequest request) throws AuthenticationException {
        args = request.getParameter("args");
        String ip = getIpAddress(request);
        if (StringUtils.isBlank(ip)) {
            return;
        }
        if (isBlackIp(ip)) {
            throw new AuthenticationException("您暂无访问权限");
        }
        String key = String.format("iprate:%s", ip);
        long now = System.currentTimeMillis();
        long start = now - 60000;

        redisTemplate.opsForZSet().removeRangeByScore(key, 0, start);

        Long count = redisTemplate.opsForZSet().zCard(key);
        // 每分钟允许ip访问的次数
        int allowCr = Integer.parseInt(NVL.nvlIfBlank(Global.getConfig("ip.request.rate"), "-1"));
        if (count != null && allowCr > -1 && count > allowCr) {
            log.info("ip 访问太频繁: {}", ip);
            addBlackList(ip);
            throw new AuthenticationException("接口访问太频繁");
        }

        redisTemplate.opsForZSet().add(key, now, now);

        // 24小时候过期
        redisTemplate.expire(key, 24, TimeUnit.HOURS);
    }

    public String getIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.isEmpty() || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress.split(",")[0];
    }

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        if (UserUtils.getUser() == null) {
            return true;
        }
        String ipAddress = getIpAddress(request);
        log.info("client ip: {}", ipAddress);

        try {
            ipLimit(request);
        } catch (AuthenticationException e) {
            JSONObject err = new JSONObject();
            err.put("result", "login");
            err.put("message", e.getMessage());
            response.setHeader("Content-Type", "application/json;charset=utf-8");
            response.getWriter().write(err.toJSONString());
            return false;
        }

        String tip = RequestLimitCache.getLimitTip(UserUtils.getUser().getId());
        if (tip != null) {
            JSONObject err = new JSONObject();
            err.put("result", "login");
            err.put("message", tip);
            response.setHeader("Content-Type", "application/json;charset=utf-8");
            response.getWriter().write(err.toJSONString());
            return false;
        }
        return true;
    }
}
